import { defineStore } from 'pinia'
import { IndexableTypeArray } from 'dexie'

import { useSlidesStore } from './slides'
import { db, Snapshot } from '@/utils/database'
import { useMainStore } from './main'


export interface ScreenState {
  snapshotCursor: number
  snapshotLength: number
}

export const useSnapshotStore = defineStore('snapshot', {
  // 规定了state中的数据返回类型为ScreenState
  state: (): ScreenState => ({
    snapshotCursor: -1, // 历史快照指针
    snapshotLength: 0 // 历史快照长度
  }),

  getters: {
    // 可撤销的前提是历史快照指针大于0
    canUndo(state) {
      return state.snapshotCursor > 0
    },
    // 可重做的前提是历史快照指针小于快照长度-1
    canRedo(state) {
      return state.snapshotCursor < state.snapshotLength - 1
    }
  },

  actions: {
    // 设置快照指针
    setSnapshotCursor(cursor: number) {
      this.snapshotCursor = cursor
    },

    // 设置快照的长度
    setSnapshotLength(length: number) {
      this.snapshotLength = length
    },

    // 初始化快照数据库
    async initSnapshotDatabase() {
      const slidesStore = useSlidesStore()
      const newFirstSnapshot = {
        index: slidesStore.slideIndex,
        slides: slidesStore.slides
      }
      await db.snapshots.add(newFirstSnapshot)
      this.setSnapshotCursor(0)
      this.setSnapshotLength(1)
    },

    // 添加快照
    async addSnapshot() {
      const slidesStore = useSlidesStore()

      /**
       * 获取当前indexedDB中的全部快照的ID
       * keys()：检索包含集合的所有键的数组（索引键或主键取决于where()子句）
       * toArray()：执行查询并获取一个数组，其结果按where()子句中使用的索引排序
       */
      const allKeys = await db.snapshots.orderBy('id').keys()

      // dexie提供的类型验证，IndexableTypeArray：索引类型的数据
      let needDeleteKeys: IndexableTypeArray = []

      /**
       * 记录需要删除的快照ID
       * 若当前快照指针不处于最后一位，那么再添加快照时，应该将当前指针位置后面的快照全部删除，对应的实际情况是：
       * 用户撤回多次后，再进行操作（添加快照），此时原先被撤销的快照都应该被删除
       */
      if (this.snapshotCursor >= 0 && this.snapshotCursor < allKeys.length - 1) {
        needDeleteKeys = allKeys.slice(this.snapshotCursor + 1)
      }

      // 添加新快照
      const snapshot = {
        index: slidesStore.slideIndex,
        slides: slidesStore.slides
      }

      // table.add()：dexie提供添加数据的方法
      await db.snapshots.add(snapshot)

      // 计算当前快照长度，用于设置快照指针的位置（此时指针应该处于最后一位，即：快照长度-1）
      let snapshotLength = allKeys.length - needDeleteKeys.length + 1
      // 快照数控超过长度限制时，应该将头部多余的快照删除
      const snapshotLengthLimit = 20
      if (snapshotLength > snapshotLengthLimit) {
        needDeleteKeys.push(allKeys[0])
        snapshotLength--
      }

      // 快照数大于1时，需要保证撤回操作后维持页面焦点不变：也就是将倒数第二个快照对应的索引设置为当前页的索引
      // https://github.com/pipipi-pikachu/PPTist/issues/27
      // if (snapshotLength >= 2) {
      //   db.snapshots.update(allKeys[snapshotLength - 2] as number, { index: slidesStore.slideIndex })
      // }

      /**
       * table.bulkDelete(keys[])：dexie提供删除数据的方法，keys[]表要删除的对象的主键数组
       * table.delete(key)：dexie提供删除数据的方法，key表示要删除的对象的主键
       */
      await db.snapshots.bulkDelete(needDeleteKeys as number[])

      // 设置快照数组的指针指向最后一个单元
      this.setSnapshotCursor(snapshotLength - 1)
      // 设置快照数组的长度
      this.setSnapshotLength(snapshotLength)
    },

    // 撤销
    async unDo() {
      // 若快照指针已经小于等于0了，则撤销功能无效
      if (this.snapshotCursor <= 0) return

      const slidesStore = useSlidesStore()
      const mainStore = useMainStore()

      // 快照指针减1
      const snapshotCursor = this.snapshotCursor - 1
      // 查询IndexedDB中的快照表
      const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
      // 将 快照指针 指向 从快照表中查询出的数组，得出撤销后（上一次）所记录的数据
      const snapshot = snapshots[snapshotCursor]
      const { index, slides } = snapshot

      // 不懂？？？
      const slideIndex = index > slides.length - 1 ? slides.length - 1 : index

      // 设置当前幻灯片页面的数据
      slidesStore.setSlides(slides)
      // 更新幻灯片的索引
      slidesStore.updateSlideIndex(slideIndex)
      // 更新当前幻灯片页面的历史快照指针
      this.setSnapshotCursor(snapshotCursor)
      // 清空处于选中状态的元素id列表
      mainStore.setActiveElementIdList([])
    },

    // 重做
    async reDo() {
      if (this.snapshotCursor >= this.snapshotLength - 1) return

      const slidesStore = useSlidesStore()
      const mainStore = useMainStore()

      const snapshotCursor = this.snapshotCursor + 1
      const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
      const snapshot = snapshots[snapshotCursor]
      const { index, slides } = snapshot

      const slideIndex = index > slides.length - 1 ? slides.length - 1 : index

      slidesStore.setSlides(slides)
      slidesStore.updateSlideIndex(slideIndex)
      this.setSnapshotCursor(snapshotCursor)
      mainStore.setActiveElementIdList([])
    }
  }
})